package com.business.wechat.action;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.business.system.service.BackFeeLogService;
import me.chanjar.weixin.common.bean.WxJsapiSignature;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpConfigStorage;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import net.sf.rose.jdbc.DBUtils;
import net.sf.rose.jdbc.service.Service;
import net.sf.rose.util.StringUtil;
import net.sf.rose.web.utils.WebUtils;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.business.system.bean.ApplyBean;
import com.business.system.bean.GoodsOrdersBean;
import com.business.system.bean.WxUserBean;
import com.business.system.service.WxUserService;
import com.business.wechat.config.DomainConfig;
import com.business.wechat.service.CoreService;
import com.business.wechat.util.ReturnModel;
import com.business.wechat.util.WxPaySignatureUtils;
import com.business.wechat.util.XMLUtil;
import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderResult;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.util.SignUtils;

/**
 * @author: xudy
 * @date: 2018/04/03 10:28
 * @description:对接微信公众号的API
 *
 */
@Controller
@RequestMapping("wechat")
public class WechatAction extends GenericController {
	@Autowired
	protected DomainConfig domainConfig;
	@Autowired
	protected WxMpConfigStorage configStorage;
	@Autowired
	protected WxMpService wxMpService;
	@Autowired
	protected WxUserService wxUserService;
	@Autowired
	protected CoreService coreService;
	@Autowired
	private WxPayConfig payConfig;
	@Autowired
	private WxPayService payService;
	@Autowired
	private BackFeeLogService backFeeLogService;

    @RequestMapping(value = "/redirectWxUrl.do")
	public String redirectWxUrl(HttpServletRequest request,HttpServletResponse response,String router,String code){
        StringBuilder redirect = new StringBuilder();
        redirect.append("redirect:/wechat/index.html?d="+new Date().getTime());
        if(router!=null){
            if(router.contains("_")){
                String routerPath = router.split("_")[0];
                redirect.append("/?#/").append(routerPath);
                String params = router.substring(router.indexOf("_")+1);
                if(params.length()>0){
                    int i =0;
                    for(String par : params.split("_")){
                        if(i==0){
                            redirect.append("?"+par.split(":")[0]+"="+par.split(":")[1]);
                        }else{
                            redirect.append("&"+par.split(":")[0]+"="+par.split(":")[1]);
                        }
                        i++;
                    }
                }
            }else{
                redirect.append("/?#/").append(router);
            }
        }
        if(code!=null){
            if(router.contains("_")){
                redirect.append("&code="+code);
            }else{
                redirect.append("?code="+code);
            }
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return redirect.toString();
    }




	/**
	 * 微信公众号webservice主服务接口，提供与微信服务器的信息交互
	 */
	@RequestMapping(value = "/core.do")
	public void wechatCore(HttpServletRequest request, HttpServletResponse response) throws Exception {
		response.setContentType("text/html;charset=utf-8");
		response.setStatus(HttpServletResponse.SC_OK);

		String signature = request.getParameter("signature");
		String nonce = request.getParameter("nonce");
		String timestamp = request.getParameter("timestamp");

		if (!this.wxMpService.checkSignature(timestamp, nonce, signature)) {
			// 消息签名不正确，说明不是公众平台发过来的消息
			response.getWriter().println("非法请求");
			return;
		}

		String echoStr = request.getParameter("echostr");
		if (StringUtils.isNotBlank(echoStr)) {
			// 说明是一个仅仅用来验证的请求，回显echostr
			String echoStrOut = String.copyValueOf(echoStr.toCharArray());
			response.getWriter().println(echoStrOut);
			return;
		}

		String encryptType = StringUtils.isBlank(request.getParameter("encrypt_type")) ? "raw" : request.getParameter("encrypt_type");

		if ("raw".equals(encryptType)) {
			// 明文传输的消息
			WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(request.getInputStream());
			WxMpXmlOutMessage outMessage = this.coreService.route(inMessage);
			if (outMessage == null) {
				response.getWriter().write("");
			} else {
				response.getWriter().write(outMessage.toXml());
			}
			return;
		}

		if ("aes".equals(encryptType)) {
			// 是aes加密的消息
			String msgSignature = request.getParameter("msg_signature");
			WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(request.getInputStream(), this.configStorage, timestamp, nonce, msgSignature);
			this.logger.debug("\n消息解密后内容为：\n{} ", inMessage.toString());
			WxMpXmlOutMessage outMessage = this.coreService.route(inMessage);
			if (outMessage == null) {
				response.getWriter().write("");
			} else {
				response.getWriter().write(outMessage.toEncryptedXml(this.configStorage));
			}

			return;
		}

		response.getWriter().println("不可识别的加密类型");
	}

	/**
	 * 通过openid获得基本用户信息 详情请见:
	 * http://mp.weixin.qq.com/wiki/14/bb5031008f1494a59c6f71fa0f319c66.html
	 *
	 * @param openid
	 *            openid
	 * @param lang
	 *            zh_CN, zh_TW, en
	 */
	@RequestMapping(value = "/getUserInfo.do")
	public WxMpUser getUserInfo(HttpServletResponse response, @RequestParam(value = "openid") String openid,
			@RequestParam(value = "lang", defaultValue = "zh_CN") String lang) {
		ReturnModel returnModel = new ReturnModel();
		WxMpUser wxMpUser = null;
		try {
			wxMpUser = this.wxMpService.getUserService().userInfo(openid, lang);
			returnModel.setResult(true);
			returnModel.setDatum(wxMpUser);
			renderString(response, returnModel);
		} catch (WxErrorException e) {
			returnModel.setResult(false);
			returnModel.setReason(e.getError().toString());
			renderString(response, returnModel);
			this.logger.error(e.getError().toString());
		}
		return wxMpUser;
	}

	/**
	 * 通过code获得基本用户信息 详情请见:
	 * http://mp.weixin.qq.com/wiki/14/bb5031008f1494a59c6f71fa0f319c66.html
	 *
	 * @param code
	 *            code
	 * @param lang
	 *            zh_CN, zh_TW, en
	 */
	@RequestMapping(value = "/getOAuth2UserInfo.do")
	public void getOAuth2UserInfo(Service service, HttpServletResponse response, @RequestParam(value = "code") String code,
			@RequestParam(value = "lang", defaultValue = "zh_CN") String lang) {
		ReturnModel returnModel = new ReturnModel();
		WxMpOAuth2AccessToken accessToken;
		WxMpUser wxMpUser;
		try {
			accessToken = this.wxMpService.oauth2getAccessToken(code);
			wxMpUser = this.wxMpService.getUserService().userInfo(accessToken.getOpenId(), lang);
			returnModel.setResult(true);
			if (wxMpUser != null) {
				try {
					WxUserBean user = wxUserService.getUserInfoByOpenId(service, wxMpUser.getOpenId());
					returnModel.setDatum(user);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			renderString(response, returnModel);
		} catch (WxErrorException e) {
			returnModel.setResult(false);
			returnModel.setReason(e.getError().toString());
			renderString(response, returnModel);
			this.logger.error(e.getError().toString());
		}
	}

	/**
	 * 用code换取oauth2的openid 详情请见:
	 * http://mp.weixin.qq.com/wiki/1/8a5ce6257f1d3b2afb20f83e72b72ce9.html
	 *
	 * @param code
	 *            code
	 */
	@RequestMapping(value = "/getOpenid.do")
	public void getOpenid(HttpServletResponse response, @RequestParam(value = "code") String code) {
		ReturnModel returnModel = new ReturnModel();
		WxMpOAuth2AccessToken accessToken;
		this.logger.info("code:" + code);
		try {
			accessToken = this.wxMpService.oauth2getAccessToken(code);
			returnModel.setResult(true);
			returnModel.setDatum(accessToken.getOpenId());
			renderString(response, returnModel);
		} catch (WxErrorException e) {
			returnModel.setResult(false);
			returnModel.setReason(e.getError().toString());
			renderString(response, returnModel);
			this.logger.error(e.getError().toString());
		}
	}

	@RequestMapping(value = "/getAccessToken.do")
	public String getAccessToken() {
		try {
			String accessToken = wxMpService.getAccessToken();
			return accessToken;
		} catch (WxErrorException e) {
			e.printStackTrace();
		}
		return null;
	}

	@RequestMapping(value = "/getJsapiTicket.do")
	@ResponseBody
	public String getJsapiTicket() {
		try {
			String jsapiTicket = wxMpService.getJsapiTicket();
			return jsapiTicket;
		} catch (WxErrorException e) {
			e.printStackTrace();
		}
		return null;
	}

	@RequestMapping(value = "/createJsapiSignature.do")
	@ResponseBody
	public WxJsapiSignature createJsapiSignature(String url) {
		try {
			WxJsapiSignature signature = wxMpService.createJsapiSignature(url);
			return signature;
		} catch (WxErrorException e) {
			e.printStackTrace();
		}
		return null;
	}

	/************************************************* 商户加盟或申请代理支付流程 ***************************************************************************/
	@RequestMapping("/applyPay.do")
	@ResponseBody
	public Map<String, String> applyPay(HttpServletRequest request, String applyInfo) {
		Map<String, String> result = new HashMap<>();
		Map<String, Object> params = WebUtils.getRequestData(request);
		ApplyBean apply = StringUtil.parse(applyInfo, ApplyBean.class);
		// 1.获取统一下单信息
		WxPayUnifiedOrderRequest unified = WxPayUnifiedOrderRequest.newBuilder().openid(params.get("openid").toString()).outTradeNo(apply.getId())
				.totalFee(BaseWxPayRequest.yuanToFen("" + apply.getApplyFee())).body(params.get("body").toString())
				.tradeType("JSAPI").spbillCreateIp(params.get("ip").toString())
				.notifyUrl(domainConfig.getDomainUrl() + "/wechat/applyPayNotify.do").build();
		unified.setSignType("MD5");
		try {
            result = buildResult(unified);
		} catch (WxPayException e) {
			e.printStackTrace();
		}
		return result;
	}

	@RequestMapping("/applyPayNotify.do")
	public void applyPayNotify(HttpServletRequest request, HttpServletResponse response) {
		try {
			synchronized (this) {
				Map<String, String> kvm = XMLUtil.parseRequestXmlToMap(request);
				if (SignUtils.checkSign(kvm, null, this.payConfig.getMchKey())) {
					if ("SUCCESS".equals(kvm.get("result_code"))) {
						String applyId = kvm.get("out_trade_no");
						Service service = new Service();
						try {
							ApplyBean apply = DBUtils.getBean(service, ApplyBean.class, applyId);
							if (apply.getPayStatus() == ApplyBean.PAY_NOT) {
							    //1.将申请表中的一些信息更新
								apply.setApplyStatus(ApplyBean.APPLY_PASS);
								apply.setPayStatus(ApplyBean.PAY_IS_PASS);
								apply.setPayType(1);
								apply.setRealFee(apply.getApplyFee());
								int i = DBUtils.update(service, apply);
								if (i > 0) {
								    //2.支付成功后进行返利
								    //2.1根据用户查询对应的推荐人
								    WxUserBean payUser = wxUserService.getUserById(service,apply.getUserId());
								    if(payUser.getFromId()!=null && !"".equals(payUser.getFromId())){
								        WxUserBean fromUser = wxUserService.getUserById(service,payUser.getFromId());
                                        boolean re =  backFeeLogService.insertApplyFeeLog(service,apply,fromUser);
                                        if(re){
                                            service.commit();
                                        }else{
                                            logger.error("插入返利日志失败!");
                                        }
                                    }

								}
							}
						} catch (Exception e) {
							service.rollback();
							e.printStackTrace();
						} finally {
							service.close();
						}
						logger.info("out_trade_no: " + kvm.get("out_trade_no") + " pay SUCCESS!");
						response.getWriter().write("<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[ok]]></return_msg></xml>");
					} else {
						this.logger.error("out_trade_no: " + kvm.get("out_trade_no") + " result_code is FAIL");
						response.getWriter().write(
								"<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[result_code is FAIL]]></return_msg></xml>");
					}
				} else {
					response.getWriter().write(
							"<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[check signature FAIL]]></return_msg></xml>");
					this.logger.error("out_trade_no: " + kvm.get("out_trade_no") + " check signature FAIL");
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
    /************************************************* 商户加盟或申请代理支付流程 ***************************************************************************/


    /************************************************* 商城支付流程*************************************************************/
    @RequestMapping("/mallPay.do")
    @ResponseBody
    public Map<String, String> mallPay(HttpServletRequest request, String orders) {
        Map<String, String> result = new HashMap<>();
        Map<String, Object> params = WebUtils.getRequestData(request);
        GoodsOrdersBean order = StringUtil.parse(orders, GoodsOrdersBean.class);
        // 1.获取统一下单信息
        WxPayUnifiedOrderRequest unified = WxPayUnifiedOrderRequest.newBuilder().openid(params.get("openid").toString()).outTradeNo(order.getId())
                .totalFee(BaseWxPayRequest.yuanToFen("" + order.getTotalFee())).body(params.get("body").toString())
                .tradeType("JSAPI").spbillCreateIp(params.get("ip").toString())
                .notifyUrl(domainConfig.getDomainUrl() + "/wechat/mallPayNotify.do").build();
        unified.setSignType("MD5");
        try {
           result = buildResult(unified);
        } catch (WxPayException e) {
            e.printStackTrace();
        }
        return result;
    }
    @RequestMapping("/mallPayNotify.do")
    public void mallPayNotify(HttpServletRequest request, HttpServletResponse response) {
        try {
            synchronized (this) {
                Map<String, String> kvm = XMLUtil.parseRequestXmlToMap(request);
                if (SignUtils.checkSign(kvm, null, this.payConfig.getMchKey())) {
                    if ("SUCCESS".equals(kvm.get("result_code"))) {
                        String orderId = kvm.get("out_trade_no");
                        Service service = new Service();
                        try {
                            GoodsOrdersBean orderBean = DBUtils.getBean(service,GoodsOrdersBean.class,orderId);
                            //如果是等待付款的状态则更改状态
                            if(orderBean.getStatus()==GoodsOrdersBean.TO_PAY) {
                                //等待发货
                                orderBean.setStatus(GoodsOrdersBean.NOT_DELIVERY);
                                orderBean.setPayTime(new Date());
                                int i = DBUtils.update(service, orderBean);
                                if (i > 0) {
                                    //插入返利
                                    WxUserBean payUser = wxUserService.getUserById(service, orderBean.getUserId());
                                    if (payUser.getFromId() != null && !"".equals(payUser.getFromId())) {
                                        WxUserBean fromUser = wxUserService.getUserById(service,payUser.getFromId());
                                        boolean result = backFeeLogService.insertRecmFeeLog(service,orderBean,fromUser);
                                        if(result){
                                            service.commit();
                                        }else{

                                        }
                                    }
                                }
                            }
                        } catch (Exception e) {
                            service.rollback();
                            e.printStackTrace();
                        } finally {
                            service.close();
                        }
                        logger.info("out_trade_no: " + kvm.get("out_trade_no") + " pay SUCCESS!");
                        response.getWriter().write("<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[ok]]></return_msg></xml>");
                    } else {
                        this.logger.error("out_trade_no: " + kvm.get("out_trade_no") + " result_code is FAIL");
                        response.getWriter().write(
                                "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[result_code is FAIL]]></return_msg></xml>");
                    }
                } else {
                    response.getWriter().write(
                            "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[check signature FAIL]]></return_msg></xml>");
                    this.logger.error("out_trade_no: " + kvm.get("out_trade_no") + " check signature FAIL");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /************************************************* 商城支付流程*************************************************************/

    private Map<String, String> buildResult(WxPayUnifiedOrderRequest unified) throws WxPayException{
        Map<String, String> result = new HashMap<>();
        WxPayUnifiedOrderResult payInfo = this.payService.unifiedOrder(unified);
        result.put("appId",this.configStorage.getAppId());
        String time = Long.toString(System.currentTimeMillis());
        result.put("timeStamp", time);
        result.put("nonceStr", payInfo.getNonceStr());
        result.put("package", "prepay_id=" + payInfo.getPrepayId());
        result.put("signType", "MD5");
        String paySign = WxPaySignatureUtils.signature(result,this.payConfig.getMchKey());
        result.put("paySign", paySign);
        return result;
    }
}